package com.example.demo.test;

import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.IntConsumer;

/**
 * @author: wukangrong
 * @date: 2020/4/22 14:17
 * @description:
 */
public class ProducerAndConsumer {
    private Queue<Integer> queue = new LinkedList<>();
    private final Lock lock = new ReentrantLock();
    private final Condition con = lock.newCondition();
    private final Condition pro = lock.newCondition();
    class Producer extends Thread {
        @Override
        public void run() {
            producer();
        }
        private void producer() {
            while(true) {
                System.out.println("生产者准备加锁");
                lock.lock();
                try {
                    if (queue.size() == 5) {
                        try {
                            System.out.println("当前队列满，生产者释放锁");
                            pro.await();
                            System.out.println("生产者被唤醒，开始生产");
                        } catch (InterruptedException ignored) {}
                    }
                    queue.add(1);
                    con.signal();
                    System.out.println("生产者" + this.getName() + "生产一条任务，当前队列长度为" + queue.size());
                    System.out.println("唤醒消费者");
                } finally {
                    lock.unlock();
                    System.out.println("生产者释放锁");
                    try {
                        Thread.sleep((int)(Math.random() * 400 + 100));
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    class Consumer extends Thread {
        @Override
        public void run() {
            consumer();
        }
        private void consumer() {
            while (true) {
                System.out.println("消费者准备加锁");
                lock.lock();
                try {
                    if (queue.size() == 0) {
                        try {
                            System.out.println("当前队列为空，消费者释放锁");
                            con.await();
                            System.out.println("消费者被唤醒，开始消费");
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    queue.poll();
                    pro.signal();
                    System.out.println("消费者" + this.getName() +  "消费一条任务，当前队列长度为" + queue.size());
                    System.out.println("唤醒生产者");
                } finally {
                    lock.unlock();
                    System.out.println("消费者释放锁");
                    try {
                        Thread.sleep((int)(Math.random() * 400 + 100));
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                }
            }
        }
    }
    public static void main(String[] args) {
//        ProducerAndConsumer pc = new ProducerAndConsumer();
//        Producer producer = pc.new Producer();
////        Producer producer2 = pc.new Producer();
//        Consumer consumer = pc.new Consumer();
////        Consumer consumer2 = pc.new Consumer();
//        producer.start();
////        producer2.start();
//        consumer.start();
//        consumer2.start();
        test();
        System.out.println("over");
    }

    private static ReentrantLock reentrantLock = new ReentrantLock();
    private static Condition one = reentrantLock.newCondition();
    private static Condition two = reentrantLock.newCondition();
    private static Condition three = reentrantLock.newCondition();
    private static volatile int num = 1;

    public static void test() {


        Thread first = new Thread(() -> {

                reentrantLock.lock();
                try {
                    for (int i = 0; i < 10; i++) {
                        // 不需要循环
                        if (num % 3 != 1){
                            one.await();
                        }
                        System.out.println(Thread.currentThread().getName() + "--" + num++);
                        // 精确唤醒
                        two.signal();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    reentrantLock.unlock();
                }


        },"first");
        Thread second = new Thread(() -> {
            reentrantLock.lock();
            try{
                for (int i = 0; i < 10; i++) {
                    if (num % 3 != 2){
                        two.await();
                    }
                    System.out.println(Thread.currentThread().getName() + "--" + num++);
                    three.signal();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                reentrantLock.unlock();
            }


        }, "second");
        Thread thrid = new Thread(() -> {
            reentrantLock.lock();
            try {
                for (int i = 0; i < 10; i++) {
                    if (num % 3 != 0){
                        three.await();
                    }
                    System.out.println(Thread.currentThread().getName() + "--" + num++);
                    one.signal();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                reentrantLock.unlock();
            }

        }, "thrid");

        first.start();
        second.start();
        thrid.start();
    }
}
class FizzBuzz {
    private int n;
    private static int count = 1;

    public FizzBuzz(int n) {
        this.n = n;
    }

    // printFizz.run() outputs "fizz".
    public void fizz(Runnable printFizz) throws InterruptedException {
        synchronized(this) {
            while(n >= count) {
                while(n >= count && count % 3 != 0 || count % 5 == 0) wait();
                if(count > n) return;
                printFizz.run();
                count++;
                notifyAll();// 由于不是精确唤醒，所以得在需要循环来实现
            }
        }

    }

    // printBuzz.run() outputs "buzz".
    public void buzz(Runnable printBuzz) throws InterruptedException {

        synchronized(this) {
            while(n >= count) {
                while(n >= count && count % 5 != 0 || count % 3 == 0) wait();
                if(count > n) return;
                printBuzz.run();
                count++;
                notifyAll();
            }
        }
    }

    // printFizzBuzz.run() outputs "fizzbuzz".
    public void fizzbuzz(Runnable printFizzBuzz) throws InterruptedException {

        synchronized(this) {
            while(n >= count) {
                while(n >= count && count % 5 != 0 || count % 3 != 0) wait();
                if(count > n) return;
                printFizzBuzz.run();
                count++;
                notifyAll();
            }
        }
    }

    // printNumber.accept(x) outputs "x", where x is an integer.
    public void number(IntConsumer printNumber) throws InterruptedException {

        synchronized(this) {
            while(n >= count) {
                while(n >= count && count % 3 == 0 || count % 5 == 0) wait();
                if(count > n) return;
                printNumber.accept(count);
                count++;
                notifyAll();
            }
        }
    }
}